/*
 * MC error interrupt handling header file. Various defines and declarations
 * across tegra chips.
 *
 * Copyright (c) 2010-2018, NVIDIA Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#ifndef __MCERR_H
#define __MCERR_H

#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/of.h>

#include <linux/platform/tegra/mc.h>

#define MAX_PRINTS			5

/* defines that are common across SOC's. */
#define MC_INTMASK			0x4

#define MC_ERR_STATUS_WRITE		(1 << 16)
#define MC_ERR_STATUS_SECURE		(1 << 17)
#define MC_ERR_STATUS_ADR_HI		(3 << 20)

#define MC_INT_DECERR_EMEM			(1<<6)
#define MC_INT_SECURITY_VIOLATION		(1<<8)
#define MC_INT_ARBITRATION_EMEM			(1<<9)
#define MC_INT_DECERR_VPR			(1<<12)
#define MC_INT_SECERR_SEC			(1<<13)
#define MC_INT_DECERR_GENERALIZED_CARVEOUT	(1<<17)

#define MC_ERR_DECERR_EMEM		(2)
#define MC_ERR_SECURITY_TRUSTZONE	(3)
#define MC_ERR_SECURITY_CARVEOUT	(4)
#define MC_ERR_INVALID_SMMU_PAGE	(6)

struct platform_device;
int tegra_mcerr_init(struct dentry *mc_paren, struct platform_device *pdev);
irqreturn_t tegra_mc_handle_general_fault(int src_chan, int intstatus);

/*
 * This describes errors that can be generated by the MC. One is defined for
 * each possibility.
 *
 * @sig Interrupt signiture for the error.
 * @msg Error description.
 * @flags Relevant flags for the error.
 * @stat_reg Register offset that holds the status of the error.
 * @addr_reg Register offset that holds the faulting address.
 */
struct mc_error {
	const char *msg;
	u32         sig;
	int         flags;
	u32         stat_reg;
	u32         addr_reg;
	u32         addr_hi_reg;
};

#define E_SMMU       (1<<0)
#define E_NO_STATUS  (1<<1) /* No status/addr */
#define E_TWO_STATUS (1<<2) /* Two status registers, no addr */
#define E_VPR        (1<<3) /* VPR violation */
#define E_ADR_HI_REG (1<<4) /* Hi Addr bits in hi reg */
#define E_GSC        (1<<5) /* GSC violation */

extern u32 mc_int_mask;
extern u32  mcerr_silenced;

struct mcerr_ops {
	/*
	 * Show the statistics for each client. This is called from a debugfs
	 * context - that means you can sleep and do general kernel stuff here.
	 */
	int (*mcerr_debugfs_show)(struct seq_file *s, void *v);

	/* Disable MC Error interrupt.
	 *
	 * Called in hard irq context to disable interrupt till
	 * soft irq handler logs the MC Error.
	 */
	void (*disable_interrupt)(unsigned int irq);

	/* Enable MC Error interrupt.
	 *
	 * Called from soft irq context after MC Error is logged.
	 */
	void (*enable_interrupt)(unsigned int irq);

	/* Clear MC Error interrupt.
	 *
	 * Called from soft irq context during MC Error print throttle.
	 */
	void (*clear_interrupt)(unsigned int irq);

	/* Log MC Error fault and clear interrupt source
	 *
	 * Called in soft irq context.
	 * As soon as interrupt status is cleared MC would be ready to
	 * hold next MC Error info.
	 */
	void (*log_mcerr_fault)(unsigned int irq);

	/* Numeric fields that must be set by the different chips. */
	unsigned int nr_clients;

	/*
	 * This array lists a string description of each valid interrupt bit.
	 * It must be at least 32 entries long. Entries that are not valid
	 * interrupts should be left as NULL. Each entry should be at most 12
	 * characters long.
	 */
	const char **intr_descriptions;
	struct mc_client *mc_clients;
};

#define client(_swgroup, _name, _swgid)					\
	{ .swgroup = _swgroup, .name = _name, .swgid = TEGRA_SWGROUP_##_swgid, }

#define dummy_client   client("dummy", "dummy", INVALID)

#define MC_ERR(_sig, _msg, _flags, _stat_reg, _addr_reg)		\
	{ .sig = _sig, .msg = _msg, .flags = _flags,			\
			.stat_reg = _stat_reg, .addr_reg = _addr_reg }

#define MC_ERR_HI(_sig, _msg, _flags, _stat_reg, _addr_reg, _addr_hi_reg) \
	{ .sig = _sig, .msg = _msg, .flags = (_flags | E_ADR_HI_REG), \
	  .stat_reg = _stat_reg, .addr_reg = _addr_reg, \
	  .addr_hi_reg = _addr_hi_reg}

#define MC_ERR_GSC(_sig, _msg, _flags, _stat_reg, _addr_reg, _addr_hi_reg) \
	{ .sig = _sig, .msg = _msg, .flags = (_flags | E_GSC), \
	  .stat_reg = _stat_reg, .addr_reg = _addr_reg, \
	  .addr_hi_reg = _addr_hi_reg}

#define mcerr_pr(fmt, ...)					\
	do {							\
		if (!mcerr_silenced) {				\
			pr_err(fmt, ##__VA_ARGS__);		\
		}						\
	} while (0)

/*
 * Error MMA tracking.
 */
#define MMA_HISTORY_SAMPLES 20
struct arb_emem_intr_info {
	int arb_intr_mma;
	u64 time;
	spinlock_t lock;
};

typedef struct mcerr_ops *(*of_mcerr_init_fn)(struct device_node *);

#define MCERR_OF_DECLARE(name, compat, fn) \
	_OF_DECLARE(mcerr, name, compat, fn, of_mcerr_init_fn)

#endif /* __MCERR_H */
